Imports System.Reflection.Emit
Imports System.Reflection
Imports System.Text.RegularExpressions

Namespace Ciloci.Flee

	''' <include file='DocComments.xml' path='DocComments/Member[@name="CalculationEngine"]/*' />	
	Public Class CalculationEngine

		Private MyDependencies As DependencyManager(Of ExpressionResultPair)
		' Map of name to node
		Private MyNameNodeMap As Dictionary(Of String, ExpressionResultPair)
		''' <include file='DocComments.xml' path='DocComments/Member[@name="CalculationEngine.NodeRecalculated"]/*' />	
		Public Event NodeRecalculated As EventHandler(Of NodeEventArgs)

		Public Sub New()
			MyDependencies = New DependencyManager(Of ExpressionResultPair)(New PairEqualityComparer())
			MyNameNodeMap = New Dictionary(Of String, ExpressionResultPair)(StringComparer.OrdinalIgnoreCase)
		End Sub

		Private Sub AddTemporaryHead(ByVal headName As String)
			Dim pair As New GenericExpressionResultPair(Of Integer)()
			pair.SetName(headName)

			If MyNameNodeMap.ContainsKey(headName) = False Then
				MyDependencies.AddTail(pair)
				MyNameNodeMap.Add(headName, pair)
			Else
				Throw New ArgumentException(String.Format("An expression already exists at '{0}'", headName))
			End If
		End Sub

		Friend Sub FixTemporaryHead(ByVal expression As IDynamicExpression, ByVal context As ExpressionContext, ByVal resultType As Type)
			Dim pairType As Type = GetType(GenericExpressionResultPair(Of ))
			pairType = pairType.MakeGenericType(resultType)

			Dim pair As ExpressionResultPair = Activator.CreateInstance(pairType)
			Dim headName As String = context.MyCalcEngineExpressionName
			pair.SetName(headName)
			pair.SetExpression(expression)

			Dim oldPair As ExpressionResultPair = MyNameNodeMap.Item(headName)
			MyDependencies.ReplaceDependency(oldPair, pair)
			MyNameNodeMap.Item(headName) = pair

			' Let the pair store the result of its expression
			pair.Recalculate()
		End Sub

		' Called by an expression when it references another expression in the engine
		Friend Sub AddDependency(ByVal tailName As String, ByVal context As ExpressionContext)
			Dim actualTail As ExpressionResultPair = Me.GetTail(tailName)
			Dim headName As String = context.MyCalcEngineExpressionName
			Dim actualHead As ExpressionResultPair = Me.GetTail(headName)

			' An expression could depend on the same reference more than once (ie: "a + a * a")
			MyDependencies.AddDepedency(actualTail, actualHead)
		End Sub

		Friend Function ResolveTailType(ByVal tailName As String) As Type
			Dim actualTail As ExpressionResultPair = Me.GetTail(tailName)
			Return actualTail.ResultType
		End Function

		Friend Function HasTail(ByVal tailName As String) As Boolean
			Return MyNameNodeMap.ContainsKey(tailName)
		End Function

		''' <include file='DocComments.xml' path='DocComments/Member[@name="CalculationEngine.Add"]/*' />	
		Public Sub Add(ByVal atomName As String, ByVal expression As String, ByVal context As ExpressionContext)
			Utility.AssertNotNull(atomName, "atomName")
			Utility.AssertNotNull(expression, "expression")
			Utility.AssertNotNull(context, "context")

			Me.AddTemporaryHead(atomName)

			context.MyCalculationEngine = Me
			context.MyCalcEngineExpressionName = atomName

			ExpressionFactory.CreateDynamic(expression, context)
		End Sub

		''' <include file='DocComments.xml' path='DocComments/Member[@name="CalculationEngine.Remove"]/*' />	
		Public Function Remove(ByVal name As String) As Boolean
			Dim tail As ExpressionResultPair = Me.GetTail(name)

			If tail Is Nothing Then
				Return False
			End If

			Dim dependents As ExpressionResultPair() = MyDependencies.GetDependents(tail)
			MyDependencies.Remove(dependents)

			For Each pair As ExpressionResultPair In dependents
				MyNameNodeMap.Remove(pair.Name)
			Next

			Return True
		End Function

		''' <include file='DocComments.xml' path='DocComments/Member[@name="CalculationEngine.CreateBatchLoader"]/*' />
		Public Function CreateBatchLoader() As BatchLoader
			Dim loader As New BatchLoader()
			Return loader
		End Function

		''' <include file='DocComments.xml' path='DocComments/Member[@name="CalculationEngine.BatchLoad"]/*' />
		Public Sub BatchLoad(ByVal loader As BatchLoader)
			Utility.AssertNotNull(loader, "loader")
			Me.Clear()

			Dim infos As BatchLoadInfo() = loader.GetBachInfos()

			For Each info As BatchLoadInfo In infos
				Me.DoBatchLoadAdd(info)
			Next
		End Sub

		Private Sub DoBatchLoadAdd(ByVal info As BatchLoadInfo)
			Try
				Me.Add(info.Name, info.ExpressionText, info.Context)
			Catch ex As ExpressionCompileException
				Me.Clear()
				Throw New BatchLoadCompileException(info.Name, info.ExpressionText, ex)
			End Try
		End Sub

		Private Function GetTail(ByVal tailName As String) As ExpressionResultPair
			Utility.AssertNotNull(tailName, "name")
			Dim pair As ExpressionResultPair = Nothing
			MyNameNodeMap.TryGetValue(tailName, pair)
			Return pair
		End Function

		Private Function GetTailWithValidate(ByVal tailName As String) As ExpressionResultPair
			Utility.AssertNotNull(tailName, "name")
			Dim pair As ExpressionResultPair = Me.GetTail(tailName)

			If pair Is Nothing Then
				Throw New ArgumentException(String.Format("No expression is associated with the name '{0}'", tailName))
			Else
				Return pair
			End If
		End Function

		Friend Sub EmitLoad(ByVal tailName As String, ByVal ilg As FleeILGenerator)
			Dim pi As PropertyInfo = GetType(ExpressionContext).GetProperty("CalculationEngine")
			ilg.Emit(OpCodes.Callvirt, pi.GetGetMethod())

			' Load the tail
			Dim methods As MemberInfo() = GetType(CalculationEngine).FindMembers(MemberTypes.Method, BindingFlags.Instance Or BindingFlags.Public, Type.FilterNameIgnoreCase, "GetResult")
			Dim mi As MethodInfo = Nothing

			For Each method As MethodInfo In methods
				If method.IsGenericMethod = True Then
					mi = method
					Exit For
				End If
			Next

			Dim resultType As Type = Me.ResolveTailType(tailName)

			mi = mi.MakeGenericMethod(resultType)

			ilg.Emit(OpCodes.Ldstr, tailName)
			ilg.Emit(OpCodes.Call, mi)
		End Sub

		''' <include file='DocComments.xml' path='DocComments/Member[@name="CalculationEngine.GetResult"]/*' />	
		Public Function GetResult(Of T)(ByVal name As String) As T
			Dim tail As ExpressionResultPair = Me.GetTailWithValidate(name)

			If Not GetType(T) Is tail.ResultType Then
				Dim msg As String = String.Format("The result type of '{0}' ('{1}') does not match the supplied type argument ('{2}')", name, tail.ResultType.Name, GetType(T).Name)
				Throw New ArgumentException(msg)
			End If

			Dim actualTail As GenericExpressionResultPair(Of T) = tail
			Return actualTail.Result
		End Function

		''' <include file='DocComments.xml' path='DocComments/Member[@name="CalculationEngine.GetResult2"]/*' />	
		Public Function GetResult(ByVal name As String) As Object
			Dim tail As ExpressionResultPair = Me.GetTailWithValidate(name)
			Return tail.ResultAsObject
		End Function

		''' <include file='DocComments.xml' path='DocComments/Member[@name="CalculationEngine.GetExpression"]/*' />	
		Public Function GetExpression(ByVal name As String) As IExpression
			Dim tail As ExpressionResultPair = Me.GetTailWithValidate(name)
			Return tail.Expression
		End Function

		''' <include file='DocComments.xml' path='DocComments/Member[@name="CalculationEngine.GetDependents"]/*' />	
		Public Function GetDependents(ByVal name As String) As String()
			Dim pair As ExpressionResultPair = Me.GetTail(name)
			Dim dependents As New List(Of ExpressionResultPair)()

			If Not pair Is Nothing Then
				MyDependencies.GetDirectDependents(pair, dependents)
			End If

			Return Me.GetNames(dependents)
		End Function

		''' <include file='DocComments.xml' path='DocComments/Member[@name="CalculationEngine.GetPrecedents"]/*' />	
		Public Function GetPrecedents(ByVal name As String) As String()
			Dim pair As ExpressionResultPair = Me.GetTail(name)
			Dim dependents As New List(Of ExpressionResultPair)()

			If Not pair Is Nothing Then
				MyDependencies.GetDirectPrecedents(pair, dependents)
			End If

			Return Me.GetNames(dependents)
		End Function

		''' <include file='DocComments.xml' path='DocComments/Member[@name="CalculationEngine.HasDependents"]/*' />	
		Public Function HasDependents(ByVal name As String) As Boolean
			Dim pair As ExpressionResultPair = Me.GetTail(name)
			Return Not pair Is Nothing AndAlso MyDependencies.HasDependents(pair)
		End Function

		''' <include file='DocComments.xml' path='DocComments/Member[@name="CalculationEngine.HasPrecedents"]/*' />	
		Public Function HasPrecedents(ByVal name As String) As Boolean
			Dim pair As ExpressionResultPair = Me.GetTail(name)
			Return Not pair Is Nothing AndAlso MyDependencies.HasPrecedents(pair)
		End Function

		''' <include file='DocComments.xml' path='DocComments/Member[@name="CalculationEngine.Contains"]/*' />	
		Public Function Contains(ByVal name As String) As Boolean
			Utility.AssertNotNull(name, "name")
			Return MyNameNodeMap.ContainsKey(name)
		End Function

		Private Function GetNames(ByVal pairs As IList(Of ExpressionResultPair)) As String()
			Dim names(pairs.Count - 1) As String

			For i As Integer = 0 To names.Length - 1
				names(i) = pairs.Item(i).Name
			Next

			Return names
		End Function

		''' <include file='DocComments.xml' path='DocComments/Member[@name="CalculationEngine.Recalculate"]/*' />	
		Public Sub Recalculate(ByVal ParamArray roots As String())
			' Get the tails corresponding to the names
			Dim rootTails As ExpressionResultPair() = Me.GetRootTails(roots)
			' Create a dependency list based on the tails
			Dim tempDependents As DependencyManager(Of ExpressionResultPair) = MyDependencies.CloneDependents(rootTails)
			' Get the sources (ie: nodes with no incoming edges) since that's what the sort requires
			Dim sources As Queue(Of ExpressionResultPair) = tempDependents.GetSources(rootTails)
			' Do the topological sort
			Dim calcList As IList(Of ExpressionResultPair) = tempDependents.TopologicalSort(sources)

			Dim args As New NodeEventArgs()

			' Recalculate the sorted expressions
			For Each pair As ExpressionResultPair In calcList
				pair.Recalculate()
				args.SetData(pair.Name, pair.ResultAsObject)
				RaiseEvent NodeRecalculated(Me, args)
			Next
		End Sub

		Private Function GetRootTails(ByVal roots As String()) As ExpressionResultPair()
			' No roots supplied so get everything
			If roots.Length = 0 Then
				Return MyDependencies.GetTails()
			End If

			' Get the tail for each name
			Dim arr(roots.Length - 1) As ExpressionResultPair

			For i As Integer = 0 To arr.Length - 1
				arr(i) = Me.GetTailWithValidate(roots(i))
			Next

			Return arr
		End Function

		''' <include file='DocComments.xml' path='DocComments/Member[@name="CalculationEngine.Clear"]/*' />	
		Public Sub Clear()
			MyDependencies.Clear()
			MyNameNodeMap.Clear()
		End Sub

		''' <include file='DocComments.xml' path='DocComments/Member[@name="CalculationEngine.Count"]/*' />	
		Public ReadOnly Property Count() As Integer
			Get
				Return MyDependencies.Count
			End Get
		End Property

		''' <include file='DocComments.xml' path='DocComments/Member[@name="CalculationEngine.DependencyGraph"]/*' />	
		Public ReadOnly Property DependencyGraph() As String
			Get
				Return MyDependencies.DependencyGraph
			End Get
		End Property
	End Class

	' Keeps track of our dependencies
	Friend Class DependencyManager(Of T)

		' Map of a node and the nodes that depend on it
		Private MyDependentsMap As Dictionary(Of T, Dictionary(Of T, Object))
		Private MyEqualityComparer As IEqualityComparer(Of T)
		' Map of a node and the number of nodes that point to it
		Private MyPrecedentsMap As Dictionary(Of T, Integer)

		Public Sub New(ByVal comparer As IEqualityComparer(Of T))
			MyEqualityComparer = comparer
			MyDependentsMap = New Dictionary(Of T, Dictionary(Of T, Object))(MyEqualityComparer)
			MyPrecedentsMap = New Dictionary(Of T, Integer)(MyEqualityComparer)
		End Sub

		Private Function CreateInnerDictionary() As IDictionary(Of T, Object)
			Return New Dictionary(Of T, Object)(MyEqualityComparer)
		End Function

		Private Function GetInnerDictionary(ByVal tail As T) As IDictionary(Of T, Object)
			Dim value As Dictionary(Of T, Object) = Nothing

			If MyDependentsMap.TryGetValue(tail, value) = True Then
				Return value
			Else
				Return Nothing
			End If
		End Function

		' Create a dependency list with only the dependents of the given tails
		Public Function CloneDependents(ByVal tails As T()) As DependencyManager(Of T)
			Dim seenNodes As IDictionary(Of T, Object) = Me.CreateInnerDictionary()
			Dim copy As New DependencyManager(Of T)(MyEqualityComparer)

			For Each tail As T In tails
				Me.CloneDependentsInternal(tail, copy, seenNodes)
			Next

			Return copy
		End Function

		Private Sub CloneDependentsInternal(ByVal tail As T, ByVal target As DependencyManager(Of T), ByVal seenNodes As IDictionary(Of T, Object))
			If seenNodes.ContainsKey(tail) = True Then
				' We've already added this node so just return
				Return
			Else
				' Haven't seen this node yet; mark it as visited
				seenNodes.Add(tail, Nothing)
				target.AddTail(tail)
			End If

			Dim innerDict As IDictionary(Of T, Object) = Me.GetInnerDictionary(tail)

			' Do the recursive add
			For Each head As T In innerDict.Keys
				target.AddDepedency(tail, head)
				Me.CloneDependentsInternal(head, target, seenNodes)
			Next
		End Sub

		Public Function GetTails() As T()
			Dim arr(MyDependentsMap.Keys.Count - 1) As T
			MyDependentsMap.Keys.CopyTo(arr, 0)
			Return arr
		End Function

		Public Sub Clear()
			MyDependentsMap.Clear()
			MyPrecedentsMap.Clear()
		End Sub

		Public Sub ReplaceDependency(ByVal old As T, ByVal replaceWith As T)
			Dim value As Dictionary(Of T, Object) = MyDependentsMap.Item(old)

			MyDependentsMap.Remove(old)
			MyDependentsMap.Add(replaceWith, value)

			For Each innerDict As Dictionary(Of T, Object) In MyDependentsMap.Values
				If innerDict.ContainsKey(old) = True Then
					innerDict.Remove(old)
					innerDict.Add(replaceWith, Nothing)
				End If
			Next
		End Sub

		Public Sub AddTail(ByVal tail As T)
			If MyDependentsMap.ContainsKey(tail) = False Then
				MyDependentsMap.Add(tail, Me.CreateInnerDictionary())
			End If
		End Sub

		Public Sub AddDepedency(ByVal tail As T, ByVal head As T)
			Dim innerDict As IDictionary(Of T, Object) = Me.GetInnerDictionary(tail)

			If innerDict.ContainsKey(head) = False Then
				innerDict.Add(head, head)
				Me.AddPrecedent(head)
			End If
		End Sub

		Public Sub RemoveDependency(ByVal tail As T, ByVal head As T)
			Dim innerDict As IDictionary(Of T, Object) = Me.GetInnerDictionary(tail)
			Me.RemoveHead(head, innerDict)
		End Sub

		Private Sub RemoveHead(ByVal head As T, ByVal dict As IDictionary(Of T, Object))
			If dict.Remove(head) = True Then
				Me.RemovePrecedent(head)
			End If
		End Sub

		Public Sub Remove(ByVal tails As T())
			For Each innerDict As Dictionary(Of T, Object) In MyDependentsMap.Values
				For Each tail As T In tails
					Me.RemoveHead(tail, innerDict)
				Next
			Next

			For Each tail As T In tails
				MyDependentsMap.Remove(tail)
			Next
		End Sub

		Public Sub GetDirectDependents(ByVal tail As T, ByVal dest As List(Of T))
			Dim innerDict As Dictionary(Of T, Object) = Me.GetInnerDictionary(tail)
			dest.AddRange(innerDict.Keys)
		End Sub

		Public Function GetDependents(ByVal tail As T) As T()
			Dim dependents As Dictionary(Of T, Object) = Me.CreateInnerDictionary()
			Me.GetDependentsRecursive(tail, dependents)

			Dim arr(dependents.Count - 1) As T
			dependents.Keys.CopyTo(arr, 0)
			Return arr
		End Function

		Private Sub GetDependentsRecursive(ByVal tail As T, ByVal dependents As Dictionary(Of T, Object))
			dependents.Item(tail) = Nothing
			Dim directDependents As Dictionary(Of T, Object) = Me.GetInnerDictionary(tail)

			For Each pair As T In directDependents.Keys
				Me.GetDependentsRecursive(pair, dependents)
			Next
		End Sub

		Public Sub GetDirectPrecedents(ByVal head As T, ByVal dest As IList(Of T))
			For Each tail As T In MyDependentsMap.Keys
				Dim innerDict As Dictionary(Of T, Object) = Me.GetInnerDictionary(tail)
				If innerDict.ContainsKey(head) = True Then
					dest.Add(tail)
				End If
			Next
		End Sub

		Private Sub AddPrecedent(ByVal head As T)
			Dim count As Integer = 0
			MyPrecedentsMap.TryGetValue(head, count)
			MyPrecedentsMap.Item(head) = count + 1
		End Sub

		Private Sub RemovePrecedent(ByVal head As T)
			Dim count As Integer = MyPrecedentsMap.Item(head) - 1

			If count = 0 Then
				MyPrecedentsMap.Remove(head)
			Else
				MyPrecedentsMap.Item(head) = count
			End If
		End Sub

		Public Function HasPrecedents(ByVal head As T) As Boolean
			Return MyPrecedentsMap.ContainsKey(head)
		End Function

		Public Function HasDependents(ByVal tail As T) As Boolean
			Dim innerDict As Dictionary(Of T, Object) = Me.GetInnerDictionary(tail)
			Return innerDict.Count > 0
		End Function

		Private Function FormatValues(ByVal values As ICollection(Of T)) As String
			Dim strings(values.Count - 1) As String
			Dim keys(values.Count - 1) As T
			values.CopyTo(keys, 0)

			For i As Integer = 0 To keys.Length - 1
				strings(i) = keys(i).ToString()
			Next

			If strings.Length = 0 Then
				Return "<empty>"
			Else
				Return String.Join(",", strings)
			End If
		End Function

		' Add all nodes that don't have any incoming edges into a queue
		Public Function GetSources(ByVal rootTails As T()) As Queue(Of T)
			Dim q As New Queue(Of T)()

			For Each rootTail As T In rootTails
				If Me.HasPrecedents(rootTail) = False Then
					q.Enqueue(rootTail)
				End If
			Next

			Return q
		End Function

		Public Function TopologicalSort(ByVal sources As Queue(Of T)) As IList(Of T)
			Dim output As IList(Of T) = New List(Of T)
			Dim directDependents As IList(Of T) = New List(Of T)

			While sources.Count > 0
				Dim n As T = sources.Dequeue()
				output.Add(n)

				directDependents.Clear()
				Me.GetDirectDependents(n, directDependents)

				For Each m As T In directDependents
					Me.RemoveDependency(n, m)

					If Me.HasPrecedents(m) = False Then
						sources.Enqueue(m)
					End If
				Next
			End While

			If output.Count <> Me.Count Then
				Throw New CircularReferenceException()
			End If

			Return output
		End Function

#If DEBUG Then
		Public ReadOnly Property Precedents() As String
			Get
				Dim list As New List(Of String)()

				For Each pair As KeyValuePair(Of T, Integer) In MyPrecedentsMap
					list.Add(pair.ToString())
				Next

				Return String.Join(System.Environment.NewLine, list.ToArray())
			End Get
		End Property
#End If

		Public ReadOnly Property DependencyGraph() As String
			Get
				Dim lines(MyDependentsMap.Count - 1) As String
				Dim index As Integer

				For Each pair As KeyValuePair(Of T, Dictionary(Of T, Object)) In MyDependentsMap
					Dim key As T = pair.Key
					Dim s As String = Me.FormatValues(pair.Value.Keys)
					lines(index) = String.Format("{0} -> {1}", key, s)
					index += 1
				Next

				Return String.Join(System.Environment.NewLine, lines)
			End Get
		End Property

		Public ReadOnly Property Count() As Integer
			Get
				Return MyDependentsMap.Count
			End Get
		End Property
	End Class

	Friend Class PairEqualityComparer
		Inherits EqualityComparer(Of ExpressionResultPair)

		Public Overloads Overrides Function Equals(ByVal x As ExpressionResultPair, ByVal y As ExpressionResultPair) As Boolean
			Return String.Equals(x.Name, y.Name, StringComparison.OrdinalIgnoreCase)
		End Function

		Public Overloads Overrides Function GetHashCode(ByVal obj As ExpressionResultPair) As Integer
			Return StringComparer.OrdinalIgnoreCase.GetHashCode(obj.Name)
		End Function
	End Class

	Friend MustInherit Class ExpressionResultPair

		Private MyName As String
		Protected MyExpression As IDynamicExpression

		Protected Sub New()

		End Sub

		Public MustOverride Sub Recalculate()

		Public Sub SetExpression(ByVal e As IDynamicExpression)
			MyExpression = e
		End Sub

		Public Sub SetName(ByVal name As String)
			MyName = name
		End Sub

		Public Overrides Function ToString() As String
			Return MyName
		End Function

		Public ReadOnly Property Name() As String
			Get
				Return MyName
			End Get
		End Property

		Public MustOverride ReadOnly Property ResultType() As Type
		Public MustOverride ReadOnly Property ResultAsObject() As Object

		Public ReadOnly Property Expression() As IDynamicExpression
			Get
				Return MyExpression
			End Get
		End Property
	End Class

	Friend Class GenericExpressionResultPair(Of T)
		Inherits ExpressionResultPair

		Public MyResult As T

		Public Sub New()

		End Sub

		Public Overrides Sub Recalculate()
			MyResult = DirectCast(MyExpression.Evaluate(), T)
		End Sub

		Public ReadOnly Property Result() As T
			Get
				Return MyResult
			End Get
		End Property

		Public Overrides ReadOnly Property ResultType() As System.Type
			Get
				Return GetType(T)
			End Get
		End Property

		Public Overrides ReadOnly Property ResultAsObject() As Object
			Get
				Return MyResult
			End Get
		End Property
	End Class

	''' <include file='DocComments.xml' path='DocComments/Member[@name="BatchLoader"]/*' />
	Public NotInheritable Class BatchLoader

		Private MyNameInfoMap As IDictionary(Of String, BatchLoadInfo)
		Private MyDependencies As DependencyManager(Of String)
		Private Shared OurCalcAtomRegex As Regex
		Private Shared OurStringLiteralRegex As Regex

		Friend Sub New()
			MyNameInfoMap = New Dictionary(Of String, BatchLoadInfo)(StringComparer.OrdinalIgnoreCase)
			MyDependencies = New DependencyManager(Of String)(StringComparer.OrdinalIgnoreCase)
		End Sub

		Shared Sub New()
			OurCalcAtomRegex = New Regex("\$[a-z_]\w*", RegexOptions.IgnoreCase)
			OurStringLiteralRegex = New Regex("""([^""\r\n\\]|\\u[0-9a-f]{4}|\\[\\""'trn])*""", RegexOptions.IgnoreCase)
		End Sub

		''' <include file='DocComments.xml' path='DocComments/Member[@name="BatchLoader.Add"]/*' />
		Public Sub Add(ByVal atomName As String, ByVal expression As String, ByVal context As ExpressionContext)
			Utility.AssertNotNull(atomName, "atomName")
			Utility.AssertNotNull(expression, "expression")
			Utility.AssertNotNull(context, "context")

			Dim info As New BatchLoadInfo(atomName, expression, context)
			MyNameInfoMap.Add(atomName, info)
			MyDependencies.AddTail(atomName)

			Dim references As String() = Me.GetReferences(expression)

			For Each reference As String In references
				MyDependencies.AddTail(reference)
				MyDependencies.AddDepedency(reference, atomName)
			Next
		End Sub

		''' <include file='DocComments.xml' path='DocComments/Member[@name="BatchLoader.Contains"]/*' />
		Public Function Contains(ByVal atomName As String) As Boolean
			Return MyNameInfoMap.ContainsKey(atomName)
		End Function

		Friend Function GetBachInfos() As BatchLoadInfo()
			Dim tails As String() = MyDependencies.GetTails()
			Dim sources As Queue(Of String) = MyDependencies.GetSources(tails)

			Dim result As IList(Of String) = MyDependencies.TopologicalSort(sources)

			Dim infos(result.Count - 1) As BatchLoadInfo

			For i As Integer = 0 To result.Count - 1
				infos(i) = MyNameInfoMap.Item(result.Item(i))
			Next

			Return infos
		End Function

		Private Function GetReferences(ByVal expression As String) As String()
			' We don't want to match anything in a string literal
			expression = OurStringLiteralRegex.Replace(expression, String.Empty)

			Dim matches As MatchCollection = OurCalcAtomRegex.Matches(expression)

			Dim arr(matches.Count - 1) As String

			For i As Integer = 0 To arr.Length - 1
				Dim value As String = matches.Item(i).Value
				value = value.Remove(0, 1)
				arr(i) = value
			Next

			Return arr
		End Function
	End Class

	Friend Class BatchLoadInfo
		Public Name As String
		Public ExpressionText As String
		Public Context As ExpressionContext

		Public Sub New(ByVal name As String, ByVal text As String, ByVal context As ExpressionContext)
			Me.Name = name
			Me.ExpressionText = text
			Me.Context = context
		End Sub
	End Class

	''' <include file='DocComments.xml' path='DocComments/Member[@name="NodeEventArgs"]/*' />	
	Public NotInheritable Class NodeEventArgs
		Inherits EventArgs

		Private MyName As String
		Private MyResult As Object

		Friend Sub New()

		End Sub

		Friend Sub SetData(ByVal name As String, ByVal result As Object)
			MyName = name
			MyResult = result
		End Sub

		''' <include file='DocComments.xml' path='DocComments/Member[@name="NodeEventArgs.Name"]/*' />	
		Public ReadOnly Property Name() As String
			Get
				Return MyName
			End Get
		End Property

		''' <include file='DocComments.xml' path='DocComments/Member[@name="NodeEventArgs.Result"]/*' />	
		Public ReadOnly Property Result() As Object
			Get
				Return MyResult
			End Get
		End Property
	End Class

	''' <include file='DocComments.xml' path='DocComments/Member[@name="BatchLoadCompileException"]/*' />	
	<Serializable()> _
	Public Class BatchLoadCompileException
		Inherits Exception

		Private MyAtomName As String
		Private MyExpressionText As String

		Friend Sub New(ByVal atomName As String, ByVal expressionText As String, ByVal innerException As ExpressionCompileException)
			MyBase.New(String.Format("Batch Load: The expression for atom '${0}' could not be compiled", atomName), innerException)
			MyAtomName = atomName
			MyExpressionText = expressionText
		End Sub

		Private Sub New(ByVal info As System.Runtime.Serialization.SerializationInfo, ByVal context As System.Runtime.Serialization.StreamingContext)
			MyBase.New(info, context)
			MyAtomName = info.GetString("AtomName")
			MyExpressionText = info.GetString("ExpressionText")
		End Sub

		Public Overrides Sub GetObjectData(ByVal info As System.Runtime.Serialization.SerializationInfo, ByVal context As System.Runtime.Serialization.StreamingContext)
			MyBase.GetObjectData(info, context)
			info.AddValue("AtomName", MyAtomName)
			info.AddValue("ExpressionText", MyExpressionText)
		End Sub

		''' <include file='DocComments.xml' path='DocComments/Member[@name="BatchLoadCompileException.AtomName"]/*' />	
		Public ReadOnly Property AtomName() As String
			Get
				Return MyAtomName
			End Get
		End Property

		''' <include file='DocComments.xml' path='DocComments/Member[@name="BatchLoadCompileException.ExpressionText"]/*' />	
		Public ReadOnly Property ExpressionText() As String
			Get
				Return MyExpressionText
			End Get
		End Property
	End Class
End Namespace